#include "component.h"
#include "device.h"
#include "dfu.h"
#ifdef HI3861_VER_INCLUDE /* 3861的工程不能添加全局字符串宏 */
#include "kds_ver.h"
#endif

#define DFU_LOG(format, ...) __OSAL_LOG("[dfu.c] " C_YELLOW format C_NONE, ##__VA_ARGS__)

// TASK EVENT
#define EVENT_DFU_RESET   0X00000001
#define EVENT_DFU_TIMEOUT 0X00000002
#define EVENT_SEND_MSG    0X00000004

#ifndef DEBUG_VERSION
#define DEBUG_VERSION "000"
#endif

// 定义GRAM DATA
__GRAM static struct
{
    uint32_t updateFlag; //OTA标识
    uint8_t devNum[4];   //需要OTA的设备编号
}gram_data;

static uint8_t *block_buffer = NULL;
static uint16_t block_size = 0;
static VirtualHardware_enum_t virtual_flash;
static uint8_t firmware_md5[32] = {0};



#if defined(KOS_PARAM_DUAL_IMAGE)
//启动DFU
static void DFU_StartApi(uint8_t *esn, uint8_t *md5)
{
    uint8_t local_esn[14] = {0};

    #ifdef KOS_PARAM_OSPORT_CRYPTO
    OSAL_OsportGetDefaultAccount(local_esn);
    #endif
    
    if (memcmp(esn, local_esn, 13) == 0 || esn[0] == 0)
    {
        DFU_LOG("DFU Start\r\n");

        //< 初始化，申请DFU_WRITE缓存
        memcpy(firmware_md5, md5, 32);
        virtual_flash = vFLASH_2;
        block_size = (uint16_t)Device_Read(virtual_flash, NULL, 0, UINT32_MAX - 2);
        DFU_LOG("DFU_StartApi md5:\r\n");
        for(uint8_t i=0;i<32;i++)
        {
            printf("%c",firmware_md5[i]);
        }
        printf("\r\n");
        DFU_LOG("block_size: %d\r\n", block_size);
        if (block_buffer == NULL)
        {
            block_buffer = (uint8_t *)OSAL_Malloc(block_size);
        }
        if (block_buffer == NULL)
        {
            DFU_LOG("block_buffer error\r\n");
            while (1);
        }

        //< 通知应用层DFU启动成功
        DfuMessage_stu_t msg = {0};
        msg.param = 0;
        msg.devNum[0] = KOS_PARAM_DFU_ALIAS;
        msg.devNum[1] = 1; //< 支持双镜像
        OSAL_MessagePublish(&msg, sizeof(msg));
        
        //< DFU启动：禁止睡眠，并设置超时10s恢复
        OSAL_SetTaskStatus(TASK_STA_ACTIVE);
        OSAL_EventSingleCreate(COMP_DFU, EVENT_DFU_TIMEOUT, 60*1000, EVT_PRIORITY_MEDIUM);
    }
}

/**
  * @brief  dfu停止
  *
  * @note
  */
static void DFU_StopApi(void)
{
    DFU_LOG("DFU_StopApi");
    OSAL_SetTaskStatus(TASK_STA_NORMAL);
    OSAL_EventDelete(COMP_DFU,EVENT_DFU_TIMEOUT);
}


#else
//设置devNum
//上位机启动OTA时会下发需要升级的devNum
//OTA结束时，应用层会将devNum清掉
static void DFU_SetDevNumApi(uint8_t devNum1, uint8_t devNum2, uint8_t devNum3, uint8_t devNum4)
{
    gram_data.devNum[0] = devNum1;
    gram_data.devNum[1] = devNum2;
    gram_data.devNum[2] = devNum3;
    gram_data.devNum[3] = devNum4;
}
#endif

//跳转APP
static void DFU_JumpAppApi(uint8_t appId)
{
#if !defined(KOS_PARAM_DUAL_IMAGE)
    if (appId == 0 && gram_data.updateFlag != UINT32_FLAG)
    {
        /* 跳转到APP0 */
        DFU_LOG("DFU Jump to app 0\r\n\r\n");
        gram_data.updateFlag = UINT32_FLAG;
        OSAL_EventSingleCreate(COMP_DFU, EVENT_DFU_RESET, 1000, EVT_PRIORITY_MEDIUM);
    }
    else if (appId == 1 && gram_data.updateFlag == UINT32_FLAG)
#else
    if (appId == 1)
#endif
    {
        /* 跳转到APP1 */
        DFU_LOG("DFU Jump to app 1\r\n\r\n");
        gram_data.updateFlag = 0;
        OSAL_EventSingleCreate(COMP_DFU, EVENT_DFU_RESET, 1000, EVT_PRIORITY_MEDIUM);
    }
}

static void DFU_VerifyFirmware(DFU_DevNum_enum_t devNum, uint32_t len)
{
    DfuMessage_stu_t msg = {0};

    //< 1:Verify ok    2:Verify failed
    msg.param = (Device_Read(virtual_flash, firmware_md5, len, UINT32_MAX - 1) == 0) ? 1 : 2;
    msg.devNum[0] = devNum;
    OSAL_MessagePublish(&msg, sizeof(msg));
}

static void DFU_Timeout(void)
{
    DfuMessage_stu_t msg = {0};

    msg.param = 3;
    OSAL_MessagePublish(&msg, sizeof(msg));
}

//写固件数据（每次写64字节）
//opt == 0：   写第0包数据
//opt == 0xFF：写第最后一包数据
static void DFU_WriteDataApi(uint8_t devNum, uint8_t opt, uint16_t len, uint8_t data[])
{
    static uint16_t block_buffer_len = 0;
    static uint32_t write_addr = 0;

    if (block_buffer == NULL)
    {
        //当前不在BootLoader里面
        return;
    }

    /* 写第0包：地址归零、申请缓存空间 */
    if (opt == 0)
    {
        write_addr = 0;
        block_buffer_len = 0;
        DFU_LOG("DFU write start, vFLASH:%04X\r\n", virtual_flash);
    }

    /* 暂存数据 */
    if (len > (block_size - block_buffer_len))
    {
        memcpy(block_buffer + block_buffer_len, data, block_size - block_buffer_len);
        len -= block_size - block_buffer_len;
        data += block_size - block_buffer_len;
        block_buffer_len += block_size - block_buffer_len;
    }
    else
    {
        memcpy(block_buffer + block_buffer_len, data, len);
        block_buffer_len += len;
        len = 0;
    }

    /* 攒够了一个block_size的数据，就写一次FLASH */
    if (block_buffer_len == block_size)
    {
        Device_Write(virtual_flash, block_buffer, block_buffer_len, write_addr);
        write_addr += block_buffer_len;
        block_buffer_len = 0;
    }

    if (len > 0)
    {
        memcpy(block_buffer + block_buffer_len, data, len);
        block_buffer_len += len;
    }

    if (opt == 0xFF)
    {
        if (block_buffer_len > 0)
        {
            Device_Write(virtual_flash, block_buffer, block_buffer_len, write_addr);
            write_addr += block_buffer_len;
            block_buffer_len = 0;
        }
        DFU_LOG("DFU write end, firmware_len: %ld byte\r\n", write_addr);
        DFU_VerifyFirmware((DFU_DevNum_enum_t)devNum, write_addr);
        write_addr = 0;
    }
}

static void DFU_ProcessMbox(uint8_t *msg)
{
    switch (msg[0])
    {
    case DFU_MBOX_JUMP_APP:
        DFU_JumpAppApi(msg[1]);
        break;

    case DFU_MBOX_WRITE_DATA:
        DFU_WriteDataApi(msg[1], msg[2], (msg[3] | (msg[4] << 8)), &msg[5]);
        OSAL_EventSingleCreate(COMP_DFU, EVENT_DFU_TIMEOUT, 10000, EVT_PRIORITY_MEDIUM);
        break;

#if defined(KOS_PARAM_DUAL_IMAGE)
    case DFU_MBOX_START:
        DFU_StartApi(&msg[1], &msg[1+13]);
        break;
    case DFU_MBOX_STOP:
        DFU_StopApi();
        break;

    case DFU_MBOX_HEAD_INFO:
        Device_Read(vFLASH_2,&msg[1],32,UINT32_MAX-3);
        break;
#else
    case DFU_MBOX_SET_DEVNUM:
        DFU_SetDevNumApi(msg[1], msg[2], msg[3], msg[4]);
        break;
#endif
    }
}

/**
 * @brief 发送消息给应用层（首次上电应用层需要的消息）
 * 
 */
static void DFU_SendMsg2Application(void)
{
    if (*((uint32_t*)(gram_data.devNum)) != 0)
    {
        DfuMessage_stu_t msg;
        msg.param = 0;
        memcpy(msg.devNum, gram_data.devNum, 4);
        OSAL_MessagePublish(&msg, sizeof(msg));
    }

    uint32_t rom_size = Device_GetDeviceCtrlBlock(vFLASH)->devParam;
    DfuMessage_stu_t rom_msg;
    rom_msg.param = 0x80; //表示上报芯片ROM-SIZE
    memcpy(rom_msg.devNum, &rom_size, 4);
    OSAL_MessagePublish(&rom_msg, sizeof(rom_msg));
}

/**
 * @brief DFU 初始化
 * 
 */
static void DFU_Init(void)
{
    __EFRAM static FlagStatus power_on = RESET;
    uint8_t flash_data[16] = {0};
    DfuNv_stu_t nvdata;

    /* 读取FLASH里面的型号信息写入NV（方便应用层获取） */
    if (power_on != SET)
    {
        power_on = SET;

        Device_Read(vFLASH_2, flash_data, 16, UINT32_MAX);
        memset(&nvdata, 0, sizeof(nvdata));
        memcpy((char*)nvdata.model, (char*)flash_data, sizeof(nvdata.model));
        memcpy((char*)nvdata.type, (char*)flash_data + 8, sizeof(nvdata.type));
        strcpy((char*)nvdata.version, KOS_VERSION DEBUG_VERSION);
        OSAL_NvWrite(0, &nvdata, sizeof(nvdata));

        if (*((uint32_t*)(gram_data.devNum)) == UINT32_MAX) // 全是FF表示是FLASH里面默认的值：直接清零
        {
            *((uint32_t*)(gram_data.devNum)) = 0;
        }

        OSAL_EventSingleCreate(COMP_DFU, EVENT_SEND_MSG, 0, EVT_PRIORITY_MEDIUM);
    }

    /* 配置DFU组件的昵称 */
    OSAL_SetCompAlias((uint8_t)KOS_PARAM_DFU_ALIAS);

    /* GRAM数据发给应用层、读取FLASH块大小 */
    if (gram_data.updateFlag == UINT32_FLAG)
    {
        virtual_flash = ((gram_data.devNum[0] % KOS_DFU_DEVNUM_MAX) == DFU_DEVNUM_AUDIO_FLASH) ? vFLASH_0 : vFLASH_2;
        block_size = (uint16_t)Device_Read(virtual_flash, NULL, 0, UINT32_MAX - 2);
        DFU_LOG("block_size: %d\r\n", block_size);
        
        block_buffer = (uint8_t *)OSAL_Malloc(block_size);
        if (block_buffer == NULL)
        {
            DFU_LOG("block_buffer error\r\n");
            while (1);
        }
    }
}

/**
  * @brief  DFU任务函数
  *
  * @note   1.任务函数内不能写阻塞代码
  *         2.任务函数每次运行只处理一个事件
  *         
  * @param  event：当前任务的所有事件
  *
  * @return 返回未处理的事件
  */
static uint32_t DFU_Task(uint32_t event)
{
    if (event & EVENT_SYS_START)
    {
        DFU_LOG("DFU Task start\r\n");
        DFU_Init();
        return (event ^ EVENT_SYS_START);
    }

    if (event & EVENT_SEND_MSG)
    {
        DFU_SendMsg2Application();
        return (event ^ EVENT_SEND_MSG);
    }

    if (event & EVENT_SYS_MBOX)
    {
        uint16_t size;
        while (OSAL_MboxLenght(&size) > 0)
        {
            uint8_t *mbox_buffer = (uint8_t *)OSAL_Malloc(size);
            if (mbox_buffer == NULL)
            {
                DFU_LOG("memory error\r\n");
                Device_EnterCritical();
                while (1);
            }
            OSAL_MboxAccept(mbox_buffer);
            DFU_ProcessMbox(mbox_buffer);
            OSAL_Free(mbox_buffer);
        }
        return (event ^ EVENT_SYS_MBOX);
    }

#if defined(KOS_PARAM_DUAL_IMAGE)
    if (event & EVENT_DFU_TIMEOUT)
    {
        DFU_LOG("DFU TIMEOUT\r\n\r\n\r\n");
        OSAL_SetTaskStatus(TASK_STA_NORMAL);
        DFU_Timeout();

        return (event ^ EVENT_DFU_TIMEOUT);
    }
#endif

    if (event & EVENT_DFU_RESET)
    {
        Device_CpuReset(1);
    }
    return 0;
}
COMPONENT_TASK_EXPORT(COMP_DFU, DFU_Task, sizeof(DfuNv_stu_t));
char import_kos_dfu;
